package com.iwdnb.gkgz.common.quota;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import com.iwdnb.bmnf.common.utils.BeanConvertUtils;
import com.iwdnb.bmnf.common.utils.BooleanUtils;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.DateUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import static com.iwdnb.gkgz.common.utils.BigDecimalUtils.ten;

/**
 * 标准版macd,与同花顺等相同
 */
public class Macd {
    public static final String QUOTA_NAME = "macd";

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class MacdData {
        private String date;
        private String datetime;
        /**
         * 12日EMA - 26日EMA
         */
        private BigDecimal dif;
        private BigDecimal dea;
        /**
         * 量柱
         */
        private BigDecimal bar;
        /**
         * 空头反转值
         */
        private BigDecimal ktReverse;
        /**
         * 多头反转值
         */
        private BigDecimal dtReverse;

        private BigDecimal price;

        public MacdData(String date, String datetime, BigDecimal dif, BigDecimal dea, BigDecimal bar) {
            this.date = date;
            this.datetime = datetime;
            this.dif = dif;
            this.dea = dea;
            this.bar = bar;
        }
    }

    private static BigDecimal calculateEMA_bak(List<BigDecimal> prices, int period) {
        BigDecimal multiplier = BigDecimal.valueOf(2).divide(BigDecimal.valueOf(period + 1), MathContext.DECIMAL128);
        BigDecimal ema = prices.get(0); // Start with the first closing price

        for (int i = 1; i < prices.size(); i++) {
            ema = (prices.get(i).subtract(ema)).multiply(multiplier).add(ema);
        }
        return BigDecimalUtils.round(ema);
    }

    public static final List<BigDecimal> calculateEMA(final List<BigDecimal> list, final int period, int scale) {
        List<BigDecimal> emaList = new ArrayList<>();
        // 开始计算EMA值，
        Double k = 2.0 / (period + 1.0);// 计算出系数
        Double ema = list.get(0).doubleValue();// 第一天ema等于当天收盘价
        emaList.add(BigDecimalUtils.of(ema, 8));
        for (int i = 1; i < list.size(); i++) {
            // 第二天以后，当天收盘价乘以系数再加上昨天EMA乘以(1-系数)
            ema = list.get(i).doubleValue() * k + ema * (1 - k);
            emaList.add(BigDecimalUtils.of(ema, 8));

        }
        return emaList;
    }

    /**
     * 计算反转数值
     *
     * @param stockDayDataList
     */
    public static void calculateReverseValue(List<StockDayData> stockDayDataList) {
        // 全局变量定义
        int Kmacd1 = 0;
        int Kmacd2 = 0;
        int dtIndex = 0; // 多头Bar位置
        double dtTempValue = 0; // 多头的临时值
        double dtValue = 0; // 多头的值
        int ktIndex = 0; // 空头Bar位置
        double ktTempValue = 0; // 空头的值
        double ktValue = 0; // 空头的值

        for (int i = 1; i < stockDayDataList.size(); i++) {
            StockDayData data = stockDayDataList.get(i);
            StockDayData beforeData = stockDayDataList.get(i - 1);
            double macd = data.getMacd().getBar().doubleValue();
            double beforeMacd = beforeData.getMacd().getBar().doubleValue();

            if (beforeMacd < 0 && macd > 0) {
                dtIndex = i;
                dtTempValue = macd;
                Kmacd1 = 1;
                Kmacd2 = 0;
            }

            if (beforeMacd > 0 && macd > 0) {
                Kmacd1++;

            }
            if (macd > beforeMacd) {
                dtIndex = i;
                dtTempValue = Math.max(dtTempValue, macd);
            }

            if (Kmacd1 == 4) {
                ktValue = ktTempValue * 0.618;
            }
            if (beforeMacd > 0 && macd < 0) {
                ktIndex = i;
                ktTempValue = macd;
                Kmacd1 = 0;
                Kmacd2 = 1;

            }
            if (beforeMacd < 0 && macd < 0) {
                Kmacd2++;
            }
            if (macd < beforeMacd) {
                ktIndex = i;
                ktTempValue = Math.min(ktTempValue, macd);
            }
            if (Kmacd2 == 4) {
                dtValue = dtTempValue * 0.618;
            }
            data.getMacd().setKtReverse(BigDecimalUtils.of(ktValue));
            data.getMacd().setDtReverse(BigDecimalUtils.of(dtValue));
            data.getMacd().setPrice(data.getClosePrice());
        }
    }

    public static List<MacdData> calculateMACD(List<StockDayData> list) {
        return calculateMACD(list, 10, 20, 7, 2);
    }

    public static List<MacdData> calculateMACD(List<StockDayData> list, int scale) {
        return calculateMACD(list, 10, 20, 7, scale);
    }

    public static List<MacdData> calculateMACD(List<StockDayData> list, int shortPeriod, int longPeriod,
        int signalPeriod, int scale) {
        List<MacdData> results = new ArrayList<>();
        List<BigDecimal> closingPrices = new ArrayList<>();

        for (StockDayData data : list) {
            closingPrices.add(data.getClosePrice());
        }

        List<BigDecimal> emaShortList = calculateEMA(closingPrices, shortPeriod, scale);
        List<BigDecimal> emaLongList = calculateEMA(closingPrices, longPeriod, scale);
        List<BigDecimal> difLine = new ArrayList<>();
        List<BigDecimal> deaLine = new ArrayList<>();
        BigDecimal two = new BigDecimal(2);
        // Calculate EMA SHORT
        int shortTemp = shortPeriod - 1;
        int begin = 0;
        BigDecimal emaShort = closingPrices.get(0);
        BigDecimal emaLong = closingPrices.get(0);
        //for (int i = 0; i < closingPrices.size(); i++) {
        //    begin = i - shortTemp;
        //    begin = begin > 0 ? begin : 0;
        //    List<BigDecimal> pricesEmaShort = closingPrices.subList(begin, i + 1);
        //    emaShort = calculateEMA(pricesEmaShort, shortPeriod, emaShort);
        //    emaShortList.add(emaShort);
        //}

        // Calculate EMA LONG
        //int longTemp = longPeriod - 1;
        //for (int i = 0; i < closingPrices.size(); i++) {
        //    begin = i - longTemp;
        //    begin = begin > 0 ? begin : 0;
        //    List<BigDecimal> pricesEmaLong = closingPrices.subList(begin, i + 1);
        //    emaLong = calculateEMA(pricesEmaLong, longPeriod, emaLong);
        //    emaLongList.add(emaLong);
        //}
        //System.out.println("emaShort:" + emaShortList);
        //System.out.println("emaLong:" + emaLongList);

        // Calculate DIFF (EMASHORT - EMALONG)
        for (int i = 0; i < closingPrices.size(); i++) {
            BigDecimal dif = emaShortList.get(i).subtract(emaLongList.get(i));
            difLine.add(dif);
        }
        deaLine = calculateEMA(difLine, signalPeriod, scale);
        // Calculate DEA line (EMA of MACD line)
        //int signalTemp = signalPeriod - 1;
        //for (int i = 0; i < difLine.size(); i++) {
        //    if (i < signalTemp) {
        //        deaLine.add(BigDecimal.ZERO); // Not enough data for Signal line
        //    } else {
        //        List<BigDecimal> subDiffList = difLine.subList(i - signalTemp, i + 1);
        //        deaLine.addAll(calculateEMA(subDiffList, signalPeriod));
        //    }
        //}

        // Create results
        for (int i = 0; i < list.size(); i++) {
            StockDayData stockDayData = list.get(i);
            BigDecimal bar = difLine.get(i).subtract(deaLine.get(i));
            bar = BigDecimalUtils.multiply(bar, two, scale);
            MacdData macdData = new MacdData(stockDayData.getDate(), stockDayData.getDatetime(),
                BigDecimalUtils.round(difLine.get(i), scale),
                BigDecimalUtils.round(deaLine.get(i), scale), bar);
            macdData.setPrice(stockDayData.getClosePrice());
            results.add(macdData);
            stockDayData.setMacd(macdData);
        }
        return results;
    }

    /**
     * 获取分时macd波段区域列表
     *
     * @param fenshiList
     * @return
     */
    public static List<MacdRegion> getFenshiMacdRegionList(List<StockDayData> fenshiList) {
        if (CollectionUtils.isEmpty(fenshiList)) {
            return null;
        }
        int index = 0;
        int maxIndex = 0;
        int minIndex = 0;
        int beginIndex = 0;
        StockDayData beginData = fenshiList.get(0);
        MacdData begin = beginData.getMacd();
        MacdData max = beginData.getMacd();
        MacdData min = beginData.getMacd();
        boolean beforeUpFlag = BigDecimalUtils.isGt(fenshiList.get(0).getMacd().getBar(), BigDecimal.ZERO);
        List<MacdRegion> macdRegionList = new ArrayList<>();
        for (int i = 0; i < fenshiList.size(); i++) {
            StockDayData data = fenshiList.get(i);
            MacdData macd = data.getMacd();
            //绿柱子，计算红柱信息
            if (beforeUpFlag && BigDecimalUtils.isLe(macd.getBar(), BigDecimal.ZERO)) {
                //下一个柱子翻红，继续看做是红柱的一部分
                if (i + 1 < fenshiList.size() - 1) {
                    StockDayData nextData = fenshiList.get(i + 1);
                    if (BigDecimalUtils.isGt(nextData.getMacd().getBar(), BigDecimal.ZERO)) {
                        continue;
                    }
                }
                //计算红柱
                if (index > 0) {
                    MacdRegion macdRegion = new MacdRegion();
                    macdRegion.setUpFlag(BooleanUtils.TRUE);
                    macdRegion.setBegin(BeanConvertUtils.convert(begin, MacdData.class));
                    macdRegion.setEnd(BeanConvertUtils.convert(fenshiList.get(i - 1).getMacd(), MacdData.class));
                    macdRegion.setLimit(BeanConvertUtils.convert(max, MacdData.class));
                    macdRegion.setDataList(fenshiList.subList(beginIndex, i).stream().map(StockDayData::getMacd)
                        .collect(Collectors.toList()));
                    macdRegion.setLength(index);
                    macdRegion.setLimitIndex(maxIndex);
                    macdRegion.setRegionValue(
                        BigDecimalUtils.multiply(max.getBar(), new BigDecimal(macdRegion.getLength())));
                    if (macdRegion.getLength() > 5) {
                        macdRegionList.add(macdRegion);
                    }
                    begin = macd;
                    beginIndex = i;
                    max = begin;
                    min = begin;
                    index = 0;
                    maxIndex = 0;
                }
            } else if (!beforeUpFlag && BigDecimalUtils.isGt(macd.getBar(), BigDecimal.ZERO)) {
                //红柱子，计算绿柱信息
                //下一个柱子翻绿，继续看做是绿柱的一部分
                if (i + 1 < fenshiList.size() - 1) {
                    StockDayData nextData = fenshiList.get(i + 1);
                    if (BigDecimalUtils.isLt(nextData.getMacd().getBar(), BigDecimal.ZERO)) {
                        continue;
                    }
                }
                //计算绿柱
                if (index > 0) {
                    MacdRegion macdRegion = new MacdRegion();
                    macdRegion.setUpFlag(BooleanUtils.FALSE);
                    macdRegion.setBegin(BeanConvertUtils.convert(begin, MacdData.class));
                    macdRegion.setEnd(BeanConvertUtils.convert(fenshiList.get(i - 1).getMacd(), MacdData.class));
                    macdRegion.setLimit(BeanConvertUtils.convert(min, MacdData.class));
                    macdRegion.setDataList(fenshiList.subList(beginIndex, i).stream().map(StockDayData::getMacd)
                        .collect(Collectors.toList()));
                    macdRegion.setLength(index);
                    macdRegion.setLimitIndex(minIndex);
                    macdRegion.setRegionValue(
                        BigDecimalUtils.multiply(min.getBar(), new BigDecimal(macdRegion.getLength())));
                    if (macdRegion.getLength() > 5) {
                        macdRegionList.add(macdRegion);
                    }
                    begin = macd;
                    beginIndex = i;
                    max = begin;
                    min = begin;
                    index = 0;
                    minIndex = 0;
                }
            } else {
                //循环处理数据
                //计算最大值和最小值
                if (BigDecimalUtils.isGt(macd.getBar(), max.getBar())) {
                    max = macd;
                    maxIndex = index;
                } else if (BigDecimalUtils.isLt(macd.getBar(), min.getBar())) {
                    min = macd;
                    minIndex = index;
                }
            }
            beforeUpFlag = BigDecimalUtils.isGt(macd.getBar(), BigDecimal.ZERO);
            index++;

        }
        //补上最后一段macd区间
        if (index > 0) {
            MacdRegion macdRegion = new MacdRegion();
            if (BigDecimalUtils.isGe(begin.getBar(), BigDecimal.ZERO)) {
                macdRegion.setUpFlag(BooleanUtils.TRUE);
                macdRegion.setLimit(max);
                macdRegion.setLength(maxIndex * 2);
            } else {
                macdRegion.setUpFlag(BooleanUtils.FALSE);
                macdRegion.setLimit(min);
                macdRegion.setLength(minIndex * 2);
            }
            MacdData limit = macdRegion.getLimit();
            macdRegion.setBegin(begin);
            macdRegion.setEnd(fenshiList.get(fenshiList.size() - 1).getMacd());
            macdRegion.setRegionValue(BigDecimalUtils.multiply(limit.getBar(), new BigDecimal(macdRegion.getLength())));
            //如果最高(低)点时间不是最后一分钟,加入到macd区域中
            if (macdRegion.getLength() > 5
                && !StringUtils.equals(limit.getDatetime(), macdRegion.getEnd().getDatetime())) {
                macdRegionList.add(macdRegion);
            }
        }

        return macdRegionList;
    }

    /**
     * 根据时间获取macd区间块
     *
     * @param macdRegionList
     * @param datetime
     * @return
     */
    public static MacdRegion getMacdRegion(List<MacdRegion> macdRegionList, String datetime) {
        return macdRegionList.stream().filter(t ->
            DateUtils.afterOrEquals(datetime, t.getBegin().getDatetime())
                && DateUtils.beforeOrEquals(datetime, t.getEnd().getDatetime())).findFirst().orElse(null);
    }

    /**
     * 根据时间获取macd上涨区间块
     *
     * @param macdRegionList
     * @param datetime
     * @return
     */
    public static MacdRegion getBeforeUpMacdRegion(List<MacdRegion> macdRegionList, String datetime) {
        for (int i = 0; i < macdRegionList.size(); i++) {
            MacdRegion macdRegion = macdRegionList.get(i);
            if (DateUtils.afterOrEquals(datetime, macdRegion.getBegin().getDatetime())
                && DateUtils.beforeOrEquals(datetime, macdRegion.getEnd().getDatetime())) {
                if (BooleanUtils.isTrue(macdRegion.getUpFlag())) {
                    return macdRegion;
                } else if (i >= 1) {
                    return macdRegionList.get(i - 1);
                } else {
                    return null;
                }
            }
        }
        return null;
    }

    /**
     * 获取macd区间块最高值衰减的第一个值
     *
     * @param macdRegion
     * @param beforeClosePrice
     * @return
     */
    public static MacdData getMacdDataAfterMax(MacdRegion macdRegion, BigDecimal beforeClosePrice) {
        List<MacdData> dataList = macdRegion.getDataList();
        if (CollectionUtils.isEmpty(dataList)) {
            return null;
        }
        MacdData maxData = dataList.get(0);
        for (MacdData macdData : macdRegion.getDataList()) {
            if (BigDecimalUtils.isGt(macdData.getBar(), maxData.getBar())) {
                maxData = macdData;
            }
        }
        //最大值小于0.005,忽略
        if (BigDecimalUtils.isLt(maxData.getBar(), BigDecimalUtils.zeroPointZeroZeroFive)) {
            return null;
        }
        int afterCount = 0;
        int lowCount = 0;
        BigDecimal maxBar = maxData.getBar();
        for (MacdData macdData : macdRegion.getDataList()) {
            if (DateUtils.beforeOrEquals(macdData.getDatetime(), maxData.getDatetime())) {
                continue;
            }
            BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(macdData.getPrice(), beforeClosePrice);
            if (BigDecimalUtils.isGe(rate, BigDecimalUtils.ninePointSix)) {
                return null;
            }
            afterCount++;
            BigDecimal macdBar = getFixMacdBarValue(macdData.getBar());
            BigDecimal macdRate = BigDecimalUtils.subStractAndDividePrecent(maxBar, macdBar);
            if (BigDecimalUtils.isGe(macdRate, ten)) {
                //macd柱缩短，且价格持续降低
                if (BigDecimalUtils.isLt(macdData.getPrice(), maxData.getPrice())) {
                    lowCount++;
                } else {
                    lowCount = 0;
                }
                if (lowCount >= 3) {
                    return macdData;
                }
            }
            if (afterCount >= 3) {
                return null;
            }
        }
        return null;
    }

    public static BigDecimal getFixMacdBarValue(BigDecimal macdBar) {
        return BigDecimalUtils.eq(macdBar, BigDecimal.ZERO) ? BigDecimalUtils.fuZeroPointZeroZeroZeroZeroOne
            : macdBar;
    }

    /**
     * macd区间
     */
    @Data
    public static class MacdRegion {
        private MacdData begin;

        private MacdData end;

        /**
         * 最高或最低值
         */
        private MacdData limit;

        private List<MacdData> dataList;

        private int length;
        /**
         * 最高或者最低的索引位置
         */
        private int limitIndex;
        /**
         * 波段值,用长度*最高柱表示
         */
        private BigDecimal regionValue;

        private String upFlag;
    }

}
